import CalcClass.CalcLexer;
import CalcClass.CalcParser;
import baseClass.BaseParseTree;
import baseClass.BaseToken;
import baseClass.ErrorParse;
import errorHandle.ErrorDeliver;
import errorHandle.LexErrorHandle;
import baseClass.ErrorToken;
import errorHandle.ParserErrorHandle;
import log4Compile.Log;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;

/*
    封装的antlr4类
    对外提供方法，隐藏错误处理的过程
    2022-3-9-杨海南
 */
public class ANTLR4 {

    private BaseParseTree parseRoot;
    private String sourceCode;
    private ParseTree parseTree;
    private ArrayList<BaseToken> commentList = new ArrayList<>();
    private ArrayList<BaseToken> tokenList = new ArrayList<>();
    private ArrayList<ErrorToken> tokenErrorList = new ArrayList<>();
    private ArrayList<ErrorParse> parseErrorList = new ArrayList<>();

    Log log = Log.getLogInstance();
    private boolean hasInit;
    //唯一实例
    private static ANTLR4 instance = new ANTLR4();

    //饿汉模式做单例
    private ANTLR4() {
    }

    //初始化，设定源代码
    public boolean initANTLR4(String sourceCode) {
        log.logger("初始化ANTLR", 3);
        if (hasInit)
            return false;
        this.sourceCode = sourceCode;
        hasInit = true;
        return true;
    }

    public static ANTLR4 getInstance() {
        return instance;
    }

    public ArrayList<ErrorToken> getTokenErrorList() {
        return tokenErrorList;
    }

    public ArrayList<ErrorParse> getParseErrorList() {
        return parseErrorList;
    }

    //产生token流和语法树，收集词法语法分析的错误信息
    public void execute(String target) {
        log.logger("开始分析源代码", 3);
        //token流产生
        CodePointCharStream cs = CharStreams.fromString(sourceCode);
        CalcLexer lexer = new CalcLexer(cs);

        //移除原本的错误处理类
        lexer.removeErrorListeners();
        lexer.addErrorListener(new LexErrorHandle());
        CommonTokenStream totalTokenStream = new CommonTokenStream(lexer);
        getFromTokenStream(totalTokenStream);
        log.logger("词法分析结束", 3);
        String tokenString = tokenString();
        log.logger("token流：" + tokenString, 1);

        CalcParser parser = new CalcParser(totalTokenStream);
        parser.removeErrorListeners();
        parser.addErrorListener(new ParserErrorHandle());
        ParseTree tree = null;

        switch (target) {
            case "program":
                tree = parser.program();
                break;
            case "expression":
                tree = parser.expression();
                break;
            case "write":
                tree = parser.write();
                break;
            case "expression_list":
                tree = parser.expression_list();
                break;
            case "type":
                tree = parser.type();
                break;
            case "variable":
                tree = parser.variable();
                break;
            case "statement":
                tree = parser.statement();
                break;
            case "statement_list":
                tree = parser.statement_list();
                break;
            case "type_declaration":
                tree = parser.type_declaration();
                break;
            case "const_declarations":
                tree = parser.const_declarations();
                break;
            case "subprogram_head":
                tree = parser.subprogram_head();
                break;
            case "var_declarations":
                tree = parser.var_declarations();
                break;
            default:
                System.err.println("未定义的操作，请至ANTLR定义");
        }
        this.parseTree = tree;

        log.logger("语法分析结束", 3);

        SelfVisitor selfVisitor = new SelfVisitor();
        selfVisitor.visit(tree);

        parser.setBuildParseTree(true);
        parseRoot = generateParseTree(tree);
        String parseString = parseString();
        log.logger("语法树：" + parseString, 1);

        this.tokenErrorList = ErrorDeliver.getErrorDeliverInstance().getTokenErrorList();
        this.parseErrorList = ErrorDeliver.getErrorDeliverInstance().getParseErrorList();

    }

    private void getFromTokenStream(CommonTokenStream tokenStream) {
        int size = tokenStream.getNumberOfOnChannelTokens() - 1;
        List<Token> tokenList = tokenStream.getTokens();
        size = tokenList.size() - 1;
        for (int i = 0; i < size; i++) {
            Token token = tokenList.get(i);
            BaseToken baseToken = new BaseToken(i, token.getText(), token.getType(), token.getStartIndex(), token.getStopIndex());
            if (token.getChannel() == 1) {
                this.commentList.add(baseToken);
            } else
                this.tokenList.add(baseToken);
        }
    }

    public ArrayList<BaseToken> getTokenList() {
        return tokenList;
    }

    public BaseParseTree getParseRoot() {
        return parseRoot;
    }

    public ArrayList<BaseToken> getCommentList() {
        return commentList;
    }

    private BaseParseTree generateParseTree(ParseTree root) {
        final String[] ruleNames = CalcParser.ruleNames;
        LinkedList<BaseParseTree> directionList = new LinkedList<>();
        LinkedList<ParseTree> sourceList = new LinkedList<>();
        sourceList.push(root);
        int levelIndex = 0;
        int rootTypeIndex = ((RuleContext) root).getRuleIndex();
        String rootType = ruleNames[rootTypeIndex];
        BaseParseTree baseRoot = new BaseParseTree(null, rootTypeIndex, rootType);
        directionList.push(baseRoot);

        while (!sourceList.isEmpty()) {
            ParseTree nowAnalysisNode = sourceList.pop();
            BaseParseTree nowInjectNode = directionList.pop();
            for (int i = 0; i < nowAnalysisNode.getChildCount(); i++) {
                ParseTree nowChild = nowAnalysisNode.getChild(i);
                if (nowChild instanceof TerminalNode) {
                    Token token = ((TerminalNode) nowChild).getSymbol();
                    BaseToken baseToken = new BaseToken(levelIndex, token.getText(), token.getType(), token.getStartIndex(), token.getStopIndex());
                    nowInjectNode.addNewChild(new BaseParseTree(nowInjectNode, baseToken));
                    levelIndex++;
                } else {
                    sourceList.push(nowChild);
                    int parseTypeIndex = ((RuleContext) nowChild).getRuleIndex();
                    String parseType = ruleNames[parseTypeIndex];
                    BaseParseTree newChild = new BaseParseTree(nowInjectNode, parseTypeIndex, parseType);
                    nowInjectNode.addNewChild(newChild);
                    directionList.push(newChild);
                }
            }
        }
        return baseRoot;
    }

    public ParseTree getParseTree() {
        return parseTree;
    }

    public String tokenString() {
        String tokenStream = null;
        //token流就是成员变量中的tokenList，已经重写了toString方法，连起来就可以
        // TODO: 2022-03-18 将token流转化为字符串
        return tokenStream;
    }

    public String parseString() {
        StringBuilder parseStream = new StringBuilder();
        //节点栈
        ArrayList<BaseParseTree> stackNode = new ArrayList<>();

        //存储孩子数量的栈，用于确定嵌套边界、以及跳出条件
        ArrayList<Integer> stackChildCount = new ArrayList<>();

        //根节点先写入输出串中
        parseStream.append(parseRoot.getParseType()).append('[');

        //获取根节点孩子数量，入栈
        int count = parseRoot.getChildCount();
        stackChildCount.add(count);

        //孩子倒序入栈
        for (int i = count; i > 0; i--) {
            stackNode.add(parseRoot.getChildren().get(i - 1));
        }

        //主循环
        while (!stackChildCount.isEmpty()) {
            //出口条件 stackChildCount栈空 跳出
            //if (stackChildCount.isEmpty())  break;

            int tempCountSize = stackChildCount.size();
            int tempChildCount = stackChildCount.get(tempCountSize - 1);

            //判断一层嵌套是否结束
            if (tempChildCount != 0) {
                stackChildCount.set(tempCountSize - 1, tempChildCount - 1);

                //取栈顶节点
                int tempNodeSize = stackNode.size();
                BaseParseTree tempNode = stackNode.get(tempNodeSize - 1);

                //是否为终结节点
                if (tempNode.isTerminal()) {
                    //判断是否需要','隔断
                    if (!parseStream.toString().endsWith("[")) {
                        parseStream.append(",");
                    }
                    //输出终结节点的token
                    parseStream.append(tempNode.getTerminalNode().getToken());
                    stackNode.remove(tempNodeSize - 1);
                } else {
                    //判断是否需要','隔断
                    if (!parseStream.toString().endsWith("[")) {
                        parseStream.append(",");
                    }
                    //输出非终结节点的ParseType
                    parseStream.append(tempNode.getParseType()).append('[');
                    //孩子数量
                    tempChildCount = tempNode.getChildCount();
                    //孩子倒序入栈
                    for (int i = tempChildCount; i > 0; i--) {
                        stackNode.add(tempNode.getChildren().get(i - 1));
                    }
                    //该节点出栈
                    stackNode.remove(tempNodeSize - 1);
                    //孩子数量入栈
                    stackChildCount.add(tempChildCount);
                }
            }
            //如果当前栈顶元素为0，说明该节点所在层无兄弟节点，一层嵌套结束，输出"]"
            else {
                parseStream.append("]");
                stackChildCount.remove(tempCountSize - 1);
            }
        }

        return parseStream.toString();
    }
}
